<?php

// ------------------------------------------------------------------------
// |@Author       : Jarmin <edshop@qq.com>
// |@----------------------------------------------------------------------
// |@Date         : 2025-04-22 15:12:33
// |@----------------------------------------------------------------------
// |@LastEditTime : 2025-04-27 22:45:13
// |@----------------------------------------------------------------------
// |@LastEditors  : Jarmin <jarmin@ladmin.cn>
// |@----------------------------------------------------------------------
// |@Description  : IP信息库
// |@----------------------------------------------------------------------
// |@FilePath     : IP.php
// |@----------------------------------------------------------------------
// |@Copyright (c) 2025 http://www.ladmin.cn   All rights reserved.
// ------------------------------------------------------------------------

namespace think\admin\service;

use think\admin\Service;
use Ip2Region;

/**
 * IP信息库
 * Class IP
 * @package think\admin\service
 */
class IP extends Service
{
    /**
     * @var Ip2Region IP地址查询实例
     */
    protected $ip2region;

    /**
     * 构造函数
     */
    public function __construct()
    {
        $this->ip2region = new Ip2Region();
    }

    /**
     * 根据IP获取地理位置信息
     *
     * @param string $ip IP地址
     * @return array 地理位置信息
     */
    public function getLocation(string $ip): array
    {
        try {
            // 默认返回信息
            $default = [
                'ip' => $ip,
                'country' => lang('service.ip_unknown'),
                'province' => lang('service.ip_unknown'),
                'city' => lang('service.ip_unknown'),
                'isp' => '',
                'full' => lang('service.ip_unknown')
            ];

            // 检查IP地址格式
            if (empty($ip) || !filter_var($ip, FILTER_VALIDATE_IP)) {
                return $default;
            }

            // 本地IP或保留IP直接返回
            if ($ip === '127.0.0.1' || $this->isInternalIp($ip)) {
                $default['country'] = lang('service.ip_intranet');
                $default['province'] = lang('service.ip_intranet');
                $default['city'] = lang('service.ip_intranet');
                $default['full'] = lang('service.ip_intranet_ip');
                return $default;
            }

            // 使用ip2region查询
            $result = $this->ip2region->memorySearch($ip);
            if (empty($result) || empty($result['region'])) {
                return $default;
            }

            // 解析返回的地区信息
            return $this->parseRegion($ip, $result['region']);
        } catch (\Exception $e) {
            // 出现异常，记录日志并返回默认值
            trace("IP地址解析异常: {$e->getMessage()}", 'error');
            return [
                'ip' => $ip,
                'country' => lang('service.ip_unknown'),
                'province' => lang('service.ip_unknown'),
                'city' => lang('service.ip_unknown'),
                'isp' => '',
                'full' => lang('service.ip_unknown')
            ];
        }
    }

    /**
     * 判断是否为内网IP
     *
     * @param string $ip IP地址
     * @return bool
     */
    public function isInternalIp(string $ip): bool
    {
        // IPv4内网地址范围
        $privateRanges = [
            ['10.0.0.0', '10.255.255.255'],     // 10.0.0.0/8
            ['172.16.0.0', '172.31.255.255'],   // 172.16.0.0/12
            ['192.168.0.0', '192.168.255.255'], // 192.168.0.0/16
            ['127.0.0.0', '127.255.255.255'],   // 127.0.0.0/8
        ];

        $longIp = ip2long($ip);
        if ($longIp === false) {
            return false;
        }

        foreach ($privateRanges as $range) {
            $start = ip2long($range[0]);
            $end = ip2long($range[1]);
            if ($longIp >= $start && $longIp <= $end) {
                return true;
            }
        }

        return false;
    }

    /**
     * 解析ip2region返回的地区信息
     *
     * @param string $ip IP地址
     * @param string $region 地区信息字符串
     * @return array 解析后的地区信息
     */
    protected function parseRegion(string $ip, string $region): array
    {
        // 格式: 国家|区域|省份|城市|ISP
        $regionParts = explode('|', $region);

        // 标准化处理
        $regionData = [
            'ip' => $ip,
            'country' => lang('service.ip_unknown'),
            'province' => lang('service.ip_unknown'),
            'city' => lang('service.ip_unknown'),
            'isp' => '',
            'full' => $region
        ];

        // 清理0值
        foreach ($regionParts as &$part) {
            if ($part === '0') {
                $part = '';
            }
        }

        // 提取各个部分
        if (!empty($regionParts[0])) {
            $regionData['country'] = $regionParts[0];
        }

        if (!empty($regionParts[2])) {
            $regionData['province'] = $regionParts[2];
        }

        if (!empty($regionParts[3])) {
            $regionData['city'] = $regionParts[3];
        }

        if (!empty($regionParts[4])) {
            $regionData['isp'] = $regionParts[4];
        }

        return $regionData;
    }

    /**
     * 获取简化后的地理位置描述
     *
     * @param string $ip IP地址
     * @return string 简化的地理位置描述
     */
    public function getLocationText(string $ip): string
    {
        $location = $this->getLocation($ip);
        $text = '';

        if ($location['country'] !== lang('service.ip_unknown') && $location['country'] !== '0') {
            $text .= $location['country'];
        }

        if ($location['province'] !== lang('service.ip_unknown') && $location['province'] !== '0' && $location['province'] !== $location['country']) {
            $text .= ($text ? ' ' : '') . $location['province'];
        }

        if ($location['city'] !== lang('service.ip_unknown') && $location['city'] !== '0' && $location['city'] !== $location['province']) {
            $text .= ($text ? ' ' : '') . $location['city'];
        }

        if (!$text) {
            $text = lang('service.ip_unknown_location');
        }

        return $text;
    }
}
